package com.uam.core.lk.utils.annotation;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.uam.core.lk.utils.ErrorInterface;
import com.uam.core.lk.utils.LogUtils;
import com.uam.core.lk.utils.ResponseResult;

/**
 * @author 祝展
 * @version 1.0
 * @description  箩筐异常码注解 处理程序
 * @Date 2019/10/31 10:38
 **/
@Aspect
@Component
public class LkErrorCodeAspect {

    private static final Logger logger = LoggerFactory.getLogger(LkErrorCodeAspect.class);

    private Map<Class, Map<Integer, ErrorInterface>> classCacheMap = new ConcurrentHashMap<>();
    /**
     * 在注解的位置切入代码
     */
    @Pointcut(value = "@annotation(com.uam.core.lk.utils.annotation.LkErrorCode)")
    public void exceptionPointCut() {
    }

    /**
     * 切面 配置通知
     *
     * @param pjp
     * @param lkerrorCode
     */
    @Around(value = "exceptionPointCut() && @annotation(lkerrorCode)")
    public Object handleException(ProceedingJoinPoint pjp, LkErrorCode lkerrorCode) throws Throwable {
        Object o = null;
        String traceId = MDC.get("TraceId");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String url = request.getRequestURL().toString();

        Signature methodName = pjp.getSignature();
        //请求的参数
        Object[] args = pjp.getArgs();
        List<Object> paramList = new ArrayList<>();
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof HttpServletRequest) {
                    continue;
                }
                paramList.add(args[i]);
            }
        }
        // 处理异常
        try {
            o = pjp.proceed();
        } catch (Exception e) {

            LogUtils.error(logger, e, "请求参数,url:{0},methodName:{1},params:{2},traceId:{3}", url, methodName.getName(),
                JSON.toJSONString(paramList),traceId);
            // 检查类
            boolean ret=false;
            Class errorClass = lkerrorCode.errorClass();
            Class<?> interfaces[] = errorClass.getInterfaces();
            for (Class<?> inte : interfaces) {
                if (inte.getName().equals("com.uam.core.lk.utils.ErrorInterface")){
                    ret = true;
                }
            }
            if ( ret == false ){
                LogUtils.error(logger, "请检查{0}枚举类是否实现了接口ErrorInterface",errorClass.getName());
                o = new ResponseResult<>(BaseErrorCode.EunmClassErrorCode);
                ((ResponseResult)o).setTraceId(traceId);
                return o;
            }
            int inErrorCode = lkerrorCode.errorCode();

            Map<Integer, ErrorInterface> eunmCache =classCacheMap.get(errorClass);
            if (eunmCache != null) {
                ErrorInterface tempErrorCode = eunmCache.get(inErrorCode);
                if (tempErrorCode != null) {
                    o = new ResponseResult<>(tempErrorCode);
                    ((ResponseResult)o).setTraceId(traceId);
                }
            }else{
                Method getErrorCode = errorClass.getMethod("getErrorCode");
                Object[] eunmList = errorClass.getEnumConstants();

                eunmCache = new HashMap<>();
                for (Object obj : eunmList) {
                    Integer tempCode = (Integer) getErrorCode.invoke(obj);
                    eunmCache.put(tempCode, (ErrorInterface) obj);
                }
                classCacheMap.put(errorClass, eunmCache);
                ErrorInterface tempErrorCode = eunmCache.get(inErrorCode);
                if (tempErrorCode != null) {
                    o = new ResponseResult<>(tempErrorCode);
                    ((ResponseResult)o).setTraceId(traceId);
                }
            }
            if (o == null) {
                LogUtils.error(logger, e, "url:{0},@LkErrorCode传递的错误码有误,检查是否存在:{1}", url, inErrorCode);
                o = new ResponseResult<>(BaseErrorCode.CheckErrorCode);
                ((ResponseResult)o).setTraceId(traceId);
            }
        } finally {
            if (o == null) {
                LogUtils.error(logger, "请检查LkErrorCodeAspect");
                o = new ResponseResult<>(BaseErrorCode.AspectErrorCode);
                ((ResponseResult)o).setTraceId(traceId);
            }
        }
        return o;
    }
}
